﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using VRacer.Packets;
using System.Windows.Threading;
using System.Windows;
using VRacer.Connection;

namespace VRacer
{
    /// <summary>
    /// Odzwierciedla gre ktora toczy sie na serwerze. Forwarduje lokalne zachowania zawodnikow do serwera za pomoca pakietu PlayerMovingPacket, oraz reaguje na jego odpowiedzi.
    /// Wlasciwa reakcja jest rozpoznawana w metodzie Connection_PacketReceived na podstawie typu pakietu, a nastepnie jest wywolywana wlasciwa metoda do obslugi tego pakietu On[TypPakietu]Packet() gdzie nastepuje juz wlasciwa reakcja.
    /// Przetrzymuje adres do serwera i ID gracza lokalnego.
    /// </summary>
    public sealed class RemoteGame : NetGame<ClientConnection, PlayerBase>, IPacketReceiver, IPacketSender
    {
        private int _id;
        private string _hostname = "localhost";
        private string _username = "defaultUser";

        public string Username
        {
            get { return _username; }
            set { _username = value; }
        }

        public RemoteGame()
        {
            this.Add(new HumanPlayer());
            Connection = new ClientConnection();
            Connection.PacketReceived += new PacketReceivedHandler(Connection_PacketReceived);
            Connection.Connected += new EventHandler(Connection_Connected);
            Connection.Disconnected += new EventHandler(Connection_Disconnected);
            Connection.Error += Connection_Error;
        }

        void Connection_Connected(object sender, EventArgs e)
        {
            Dispatcher.BeginInvoke((DefaultDelegate)(() =>
                {
                    InitializeInternal();
                }));
        }

        void Connection_Error(object sender, ErrorEventArgs e)
        {
            this.State = GameState.Uninitialized;
        }

        void Connection_Disconnected(object sender, EventArgs e)
        {
            End();
        }

        public string HostName
        {
            get { return _hostname; }
            set { _hostname = value; }
        }


        public int ID
        {
            get { return _id; }
            set { _id = value; }
        }


        public override void Add(PlayerBase player)
        {
            PlayersByID.Add(player.ID, player);

            Dispatcher.BeginInvoke((DefaultDelegate)(() =>
            {
                base.Add(player);
            }));
        }

        protected override bool HandleNewBonus(IBonus bonus)
        {
            return true;
        }

        void Connection_PacketReceived(object sender, PacketBase packet)
        {
            Dispatcher.BeginInvoke((DefaultDelegate)(() =>
            {
                switch (packet.Type)
                {
                    case PacketType.Welcome:
                        OnWelcomePacket((WelcomePacket)packet);
                        break;
                    case PacketType.NewPlayer:
                        OnNewPlayerPacket((NewPlayerPacket)packet);
                        break;
                    case PacketType.GameState:
                        OnGameStatePacket((GameStatePacket)packet);
                        break;
                    case PacketType.PlayerMoved:
                        OnPlayerMovedPacket((PlayerMovedPacket)packet);
                        break;
                    case PacketType.PlayerCrashed:
                        OnPlayerCrashedPacket((PlayerCrashedPacket)packet);
                        break;
                    case PacketType.ContinueTurn:
                        OnContinueTurnPacket((ContinueTurnPacket)packet);
                        break;
                    case PacketType.PlayerCollision:
                        OnPlayerCollisionPacket((PlayerCollisionPacket)packet);
                        break;
                    case PacketType.PlayerRemoved:
                        OnPlayerRemovedPacket((PlayerRemovedPacket)packet);
                        break;
                    case PacketType.PlayerPosition:
                        OnPlayerPositionPacket((PlayerPositionPacket)packet);
                        break;
                    case PacketType.Scores:
                        Scores = ((ScoresPacket)packet).Scores;
                        break;
                    case PacketType.Message:
                        OnMessagePacket((MessagePacket)packet);
                        break;
                    case PacketType.PlayerLaps:
                        OnPlayerLapsPacket((PlayerLapsPacket)packet);
                        break;
                }
            }));
        }

        





        #region Packet handlers

        private void OnContinueTurnPacket(ContinueTurnPacket continueTurn)
        {
            var player = PlayersByID[continueTurn.NextPlayerID];

            if (player != null)
            {
                int i = continueTurn.Turns;
                while (i-- > 0)
                    StartNewTurn();

                ContinueTurn(player);
            }
        }

        private void OnGameStatePacket(GameStatePacket gameStart)
        {
            switch (gameStart.State)
            {
                case GameState.Ended:
                    End();
                    break;
                case GameState.Finished:
                    Finish();
                    break;
                case GameState.Started:
                    this.State = GameState.Started;
                    break;
            }
        }

        private void OnMessagePacket(MessagePacket messagePacket)
        {

            PlayerBase from = PlayersByID[messagePacket.ID];
            if (from != null)
            {
                OnMessage(new Message(from.Title, messagePacket.Message));
            }
        }

        private void OnNewPlayerPacket(NewPlayerPacket newPlayer)
        {
            if (ID != newPlayer.ID)
            {
                var player = new RemotePlayer(newPlayer);
                Add(player);
            }
        }

        private void OnPlayerCollisionPacket(PlayerCollisionPacket playerCollision)
        {
            var uderzajacy = PlayersByID[playerCollision.ID];
            var uderzony = PlayersByID[playerCollision.HitPlayerID];

            if (uderzajacy != null && uderzony != null)
            {
                HandlePlayerCollision(uderzajacy, uderzony, playerCollision.CollisionPoint);
            }
        }

        private void OnPlayerCrashedPacket(PlayerCrashedPacket playerCrashed)
        {
            var player = PlayersByID[playerCrashed.ID];

            if (player != null)
            {
                player.Moves -= playerCrashed.LostMoves;
                player.Crash(playerCrashed.CrashPoint, playerCrashed.StartPoint);
                player.Stop();
            }
        }

        private void OnPlayerLapsPacket(PlayerLapsPacket playerLaps)
        {
            var player = PlayersByID[playerLaps.ID];
            if (player != null)
                player.Laps = playerLaps.Laps;
        }

        private void OnPlayerMovedPacket(PlayerMovedPacket playerMoved)
        {
            var player = PlayersByID[playerMoved.ID];

            if (player != null)
            {
                player.MoveInternal(playerMoved.NewPosition);
                player.Statistics.Moves++;
            }
        }

        private void OnPlayerPositionPacket(PlayerPositionPacket playerPosition)
        {
            var player = PlayersByID[playerPosition.ID];

            if (player != null)
            {
                player.Position = (Point)playerPosition.Position;
            }
        }

        private void OnPlayerRemovedPacket(PlayerRemovedPacket playerRemoved)
        {
            var player = PlayersByID[playerRemoved.ID];

            if (player != null)
            {
                Remove(player);
            }
        }

        private void OnWelcomePacket(WelcomePacket welcome)
        {
            Players.Clear();
            PlayersByID.Clear();
            var player = new HumanPlayer();
            player.ID = welcome.ID;
            player.Title = _username;

            this.Add(player);

            ID = welcome.ID;
            var newTrack = Tracks[welcome.TrackID];

            if (newTrack == null)
                Connection.Disconnect(new Exceptions.RemoteGameWrongTrackException(null));
            else
                CurrentTrack = newTrack;

            Send(new LoginPacket(welcome) { Name = _username });
        }

        #endregion

        protected override void LoadTracks()
        {
            Tracks = TracksCollection.Create("Tracks/");
        }



        protected override void OnPlayerCollision(PlayerBase player, PlayerCollisionEventArgs e)
        {

        }

        protected override void OnPlayerMoved(PlayerBase player)
        {

        }

        protected override void OnPlayerMoving(PlayerBase player, PlayerMovingEventArgs e)
        {
            e.CanMove = false;
            if (player.ID == ID)
                Send(new PlayerMovingPacket(e.NewPosition));
        }

        protected override void OnPlayerCrashed(PlayerBase player, PlayerCrashedEventArgs e)
        {

        }

        private void InitializeInternal()
        {
            base.Initialize();
        }

        public override void Initialize()
        {
            Connection.Connect(HostName, Port);
        }

        public override void Start()
        {
            Send(new GameStatePacket());
        }

        public void SendMessage(string message)
        {
            Send(new MessagePacket(ID, message));
        }

        #region IPacketReceiver Members

        public event PacketReceivedHandler PacketReceived
        {
            add { Connection.PacketReceived += value; }
            remove { Connection.PacketReceived -= value; }
        }

        #endregion

        #region IPacketSender Members

        public event PacketSentHandler PacketSent
        {
            add { Connection.PacketSent += value; }
            remove { Connection.PacketSent -= value; }
        }

        public override void Send(PacketBase packet)
        {
            Connection.Send(packet);
        }

        #endregion
    }
}
